迷宫类问题:
经典模型:给定一个地图(地图由可到达的空地和不可到达的墙组成),一个起点和一个终点,问从起点到终点的最短路程为多少
一般是使用带结构体的队列配合BFS进行操作,结构体包含每一个结点的各种信息,使用vis数组防止重复访问同一节点
将起点作为初始结点,加入队列,每次取队头作为扩展结点,向四个方向扩展,扩展的条件是新的点位置可以访问(不越界且非墙)且该点未被遍历,直到队列为空或者某一点为终点,不断更新结构体上的信息如当前步数等,得出最终解
变形一: 要求输出路径
如果沿用之前的方法要想在结构体上去维护路径是比较困难的,这里可以考虑用一个数组fa[x][y]来记录xy点的父亲结点,之后使用递归的方法则可以输出路径
这里可以采用数组来模拟队列,并且对于经典模型的迷宫问题,我们可以用一个变量表示一个点,把xy通过变换变为一个变量,代码如下
#include <cstring>
#include <cstdio>
const int maxn=105;
int n,m; //行数和列数
int ix,iy,tx,ty; //初始位置和终点位置
int maze[maxn][maxn]; //maze[x][y]表示xy是否能被访问
int fa[maxn][maxn]; //fa[x][y]表示xy之前的那个点
int dist[maxn][maxn]; //dist[x][y]表示走到xy这一点用了多少步
int last_dir[maxn][maxn]; //last_dir[x][y]表示xy这一点是由fa[x][y]从哪个方向走到的
bool vis[maxn][maxn]; //vis[x][y]表示xy点是否遍历过
int Q[maxn*maxn]; //用Q来模拟队列 给定两个下标 front和rear 那么入队则是Q[rear++]=u 出队是u=Q[front++]
int dir[maxn*maxn]; //用dir来模拟堆栈 保存路径
int dx[4]={-1,0,1,0}; //与dy[]一起模拟方向 依次是上左下右
int dy[4]={0,-1,0,1};
char name[4]={'U','L','D','R'};
bool bfs(int x,int y)
{
int front,rear;
int d,u;
u=x*m+y; //这里使用压缩技术节省空间 用一个变量表示xy一个坐标 将x坐标扩大m倍(因为y不可能超过m) 因此解压时x=u/m y=u%m
rear=front=0;
vis[x][y]=true;
fa[x][y]=u;
dist[x][y]=0;
Q[rear++]=u;
while(rear>front)
{
u=Q[front++];
x=u/m;
y=u%m;
for (d=0;d<4;d++)
{
int nx=x+dx[d];
int ny=y+dy[d];
if (nx>=0 && nx<n && ny>=0 && ny<m && maze[nx][ny] && !vis[nx][ny]) //生成新扩展结点的三个要求 不越界 未遍历 能访问
{
int v=nx*m+ny;
Q[rear++]=v;
vis[nx][ny]=true;
fa[nx][ny]=u;
dist[nx][ny]=1+dist[x][y];
last_dir[nx][ny]=d;
if (nx==tx && ny==ty)
{
return true;
}
}
}
}
return false;
}
void print_path(int x,int y)
{
int c=0;
while(true)
{
int u=fa[x][y];
int fx=u/m;
int fy=u%m;
if (fx==x && fy==y)
{
break;
}
dir[c++]=last_dir[x][y];
x=fx;
y=fy;
}
while(c--)
{
putchar(name[dir[c]]);
}
}
int main()
{
int i,j,te,t;
scanf("%d",&t);
while(t--)
{
memset(vis,false,sizeof(vis));
scanf("%d%d",&n,&m);
for (i=0;i<n;i++)
{
for (j=0;j<m;j++)
{
scanf("%d",&maze[i][j]);
}
}
scanf("%d%d",&ix,&iy);
scanf("%d%d",&tx,&ty);
bfs(ix,iy);
print_path(tx,ty);
}
return 0;
}